Skip to content
lab components / Forms and input

File Input

Allows users to select and upload files from their devices. Used in forms, applications, or processes where user-provided files are necessary.

This is a Lab component!

That means it doesn't satisfy our definition of done and may be changed or even deleted. For an exact status, please reach out to the Fancy team through the dev_fancy or ux_fancy channels.

import { FileInput } from "@siteimprove/fancylab";

#Examples

#Default

The standard way to let users upload a single file. It shows a button that triggers the device's file selection dialog. After selection, displays the chosen file name.

const [files, setFiles] = useState<FileList | null>(null); return ( <FileInput value={files} onChange={(x: FileList | null) => setFiles(x)}> Choose file </FileInput> );

#Don't List Files

Ideal when file names are irrelevant to the user's task.

const [files, setFiles] = useState<FileList | null>(null); useEffect(() => { console.log(files); }, [files]); return ( <FileInput listFiles={false} value={files} onChange={(x: FileList | null) => setFiles(x)}> Choose file </FileInput> );

#Hide Button

Best Practice:

  • Embed the input within a custom element.
  • Visually indicate the upload zone with clear instructions and cues.
const [files, setFiles] = useState<FileList | null>(null); const hasFiles = files != undefined && files != null && files.length > 0; return ( <FileInput hideButton={hasFiles} value={files} onChange={(x: FileList | null) => setFiles(x)}> Choose file </FileInput> );

#Multiple Files

When users need to upload multiple files at once.

Best Practice:

  • Allow users to select several files simultaneously.
  • Display the number of selected files and their names in a legible list.
  • Clearly state the maximum number of permitted files.
const [files, setFiles] = useState<FileList | null>(null); return ( <FileInput multiple value={files} onChange={(x: FileList | null) => setFiles(x)}> Choose files </FileInput> );

#File Type

When you want to restrict the types of files users can upload (e.g., images only).

Best Practice:

  • Specify acceptable file types (e.g., png, jpg)
const [files, setFiles] = useState<FileList | null>(null); return ( <FileInput accept=".png, .jpg, .jpeg" value={files} onChange={(x: FileList | null) => setFiles(x)} > Choose file </FileInput> );

#Form Element Wrapper

Integrate the file input seamlessly into forms, aligning with other form elements.

Best Practice:

  • Apply consistent styling with other form controls (input fields, buttons, etc.).
  • Ensure proper spacing and alignment within the form.
  • Provide informative error messages if uploads fail or file types are invalid.
const [files, setFiles] = useState<FileList | null>(null); const hasFiles = files != undefined && files != null && files.length > 0; return ( <FormElementWrapper name="file" label="Choose File" error={"Selected file invalid"} invalid={hasFiles && files[0].size > 1000} > <FileInput value={files} onChange={(x: FileList | null) => setFiles(x)}> <Icon> <IconAdd /> </Icon> Add </FileInput> </FormElementWrapper> );

#Variant

Choose the appearance of the file input button to match your design.

Best Practice:

  • Prioritize accessibility and maintain clear "button" affordance.
  • See Button variant
const [filesPrimary, setFilesPrimary] = useState<FileList | null>(null); const [filesSecondary, setFilesSecondary] = useState<FileList | null>(null); const [filesDefault, setFilesDefault] = useState<FileList | null>(null); const [filesBorderless, setFilesBorderless] = useState<FileList | null>(null); const [filesCtaPrimary, setFilesCtaPrimary] = useState<FileList | null>(null); const [filesCtaSecondary, setFilesCtaSecondary] = useState<FileList | null>(null); const [filesCtaDefault, setFilesCtaDefault] = useState<FileList | null>(null); return ( <> <div style={{ display: "block", marginBottom: "1rem" }}> <FileInput variant="primary" value={filesPrimary} onChange={(x: FileList | null) => setFilesPrimary(x)} > Primary </FileInput> </div> <div style={{ display: "block", marginBottom: "1rem" }}> <FileInput variant="secondary" value={filesSecondary} onChange={(x: FileList | null) => setFilesSecondary(x)} > Secondary </FileInput> </div> <div style={{ display: "block", marginBottom: "1rem" }}> <FileInput value={filesDefault} onChange={(x: FileList | null) => setFilesDefault(x)}> Default </FileInput> </div> <div style={{ display: "block", marginBottom: "1rem" }}> <FileInput variant="borderless" value={filesBorderless} onChange={(x: FileList | null) => setFilesBorderless(x)} > Borderless </FileInput> </div> <div style={{ display: "block", marginBottom: "1rem" }}> <FileInput variant="ctaPrimary" value={filesCtaPrimary} onChange={(x: FileList | null) => setFilesCtaPrimary(x)} > Primary </FileInput> </div> <div style={{ display: "block", marginBottom: "1rem" }}> <FileInput variant="ctaSecondary" value={filesCtaSecondary} onChange={(x: FileList | null) => setFilesCtaSecondary(x)} > Secondary </FileInput> </div> <div style={{ display: "block", marginBottom: "1rem" }}> <FileInput variant="ctaDefault" value={filesCtaDefault} onChange={(x: FileList | null) => setFilesCtaDefault(x)} > Default </FileInput> </div> </> );

#Sizes

Adjust the size of the file input to fit different contexts and layouts.

Best Practice:

  • Adjust the file input's size for different layouts and screen sizes.
  • Maintain visual consistency with other elements.
  • Ensure the input is easily clickable on all devices.
const [filesSmall, setFilesSmall] = useState<FileList | null>(null); const [filesMedium, setFilesMedium] = useState<FileList | null>(null); const [filesLarge, setFilesLarge] = useState<FileList | null>(null); return ( <> <div style={{ display: "block", marginBottom: "1rem" }}> <FileInput size="small" value={filesSmall} onChange={(x: FileList | null) => setFilesSmall(x)} > Small </FileInput> </div> <div style={{ display: "block", marginBottom: "1rem" }}> <FileInput size="medium" value={filesMedium} onChange={(x: FileList | null) => setFilesMedium(x)} > Medium </FileInput> </div> <div style={{ display: "block", marginBottom: "1rem" }}> <FileInput size="large" value={filesLarge} onChange={(x: FileList | null) => setFilesLarge(x)} > Large </FileInput> </div> </> );

#Full Width

Maximize the width of the file input, especially in wide content areas.

Best Practice:

  • Expand the input to the full width of its container.
  • Use cautiously, especially on smaller screens.
const [files, setFiles] = useState<FileList | null>(null); return ( <FileInput fullWidth value={files} onChange={(x: FileList | null) => setFiles(x)}> Choose file </FileInput> );

#Loading

Provide visual feedback while the file is being uploaded. Use Spinner to clearly communicate the upload status to the user.

const [files, setFiles] = useState<FileList | null>(null); return ( <> <div style={{ display: "inline-block", marginRight: "0.5rem" }}> <FileInput loading value={files} onChange={(x: FileList | null) => setFiles(x)}> Choose file </FileInput> </div> <div style={{ display: "inline-block", marginRight: "0.5rem" }}> <FileInput loading variant="ctaDefault" value={files} onChange={(x: FileList | null) => setFiles(x)} > Choose file </FileInput> </div> <div style={{ display: "inline-block", marginRight: "0.5rem" }}> <FileInput loading value={files} onChange={(x: FileList | null) => setFiles(x)}> <Icon> <IconAdd /> </Icon> </FileInput> </div> </> );

#Disabled

Prevent user interaction when the file input is not available.

const [files, setFiles] = useState<FileList | null>(null); return ( <> <div style={{ display: "inline-block", marginRight: "0.5rem" }}> <FileInput disabled value={files} onChange={(x: FileList | null) => setFiles(x)}> Disabled </FileInput> </div> <div style={{ display: "inline-block", marginRight: "0.5rem" }}> <FileInput disabled variant="ctaDefault" value={files} onChange={(x: FileList | null) => setFiles(x)} > Disabled </FileInput> </div> <div style={{ display: "inline-block", marginRight: "0.5rem" }}> <FileInput disabled ariaLabel="Add file" value={files} onChange={(x: FileList | null) => setFiles(x)} > <Icon> <IconAdd /> </Icon> </FileInput> </div> </> );

#Properties

PropertyDescriptionDefinedValue
valueRequired
| objectValue of the form control
onChangeRequired
functionCallback for onChange event
nameOptional
stringName applied to the form control
idOptional
stringId applied to the form control
invalidOptional
booleanIs the form control invalid
onBlurOptional
functionCallback for onBlur event
aria-labelOptional
stringLabel of the form control
aria-describedbyOptional
stringID of an an element that describes what the form control is for
aria-labelledbyOptional
stringID of an an element that labels this form control
childrenOptional
elementText and icons to be displayed inside the button.
listFilesOptional
booleanShould selected files be listed on screen
hideButtonOptional
booleanShould the button be hidden
multipleOptional
booleanAllow multiple files to be selected
acceptOptional
stringSpecify accepted file extensions
disabledOptional
booleanCan the file input button be clicked
sizeOptional
"large" | "medium" | "small"Controls the size of the button and listed files - defaults to medium
fullWidthOptional
booleanShould the file input button fill full width?
variantOptional
"borderless" | "ctaDefault" | "ctaPrimary" | "ctaSecondary" | "default" | "destructive" | "primary" | "secondary"How should the file input button look
loadingOptional
booleanShould the file input button show a loading indicator
autoFocusOptional
booleanFocus the file input immediately when rendering the first time
ariaHiddenOptional
booleanIndicates whether the element is exposed to an accessibility API.
ariaLabelOptional
stringDescribe what happens if the file input is clicked
ariaLabelledByOptional
stringID of an element that describes what happens if the file input is clicked
ariaDescribedByOptional
stringIDs of the elements that describe the file input's function
classNameOptional
stringCustom className that's applied to the outermost element (only intended for special cases)
styleOptional
objectStyle object to apply custom inline styles (only intended for special cases)
tabIndexOptional
numberTab index of the outermost HTML element of the component
onKeyDownOptional
functionCallback for onKeyDown event
onMouseDownOptional
functionCallback for onMouseDown event
onMouseEnterOptional
functionCallback for onMouseEnter event
onMouseLeaveOptional
functionCallback for onMouseLeave event
onFocusOptional
functionCallback for onFocus event
data-componentOptional
stringName of the component. Should only be set by components since it needs to stable. Used to track component usage
data-observe-keyOptional
stringUnique string, used by external script e.g. for event tracking

#Guidelines

#Best practices

#General

Use FileInput when users need to upload files from their device as part of a form, application, or process.

#Placement

FileInput is typically used in the following places:

  • Form: When file uploads are part of a structured data entry process. Example can be found in Uploading a logo while creating a new email template in a dashboard. (e.g New email template).
  • Modal/Dialogs: To streamline the upload experience without navigating away from the current page.
  • Card: To provide a focused area for file interaction in dedicated upload sections.
    • Example: A "Bulk keyword import" section in SEO settings where users can upload files.
    • Example: Uploading a site list within a card in Setting > Add sites

#Style

  • Siteimprove Design System: Adhere to Siteimprove's guidelines for color, typography, and spacing. If you are not using a component from Fancy, match the styling of your FileInput to existing components for visual consistency.

#Do not use when

Avoid using for large file uploads. Instead, consider these alternatives for a better user experience:

  • Break large files into smaller pieces for more reliable and manageable uploads. This also enables better error handling and recovery.
  • Provide clear visual feedback during uploads, including a progress bar, estimated time remaining, and the option to cancel the upload if needed.

#Accessibility

#For designers

  • Ensure sufficient color contrast between text and background.
  • Provide clear instructions and error messages.

#For developers

This component comes with built-in accessibility, no extra work required.

Explore detailed guidelines for this component: Accessibility Specifications

#Writing

  • Use clear, concise labels (e.g., "Upload File").
  • Avoid technical jargon.